package aorm

import (
	"errors"
	"reflect"
)

type Orm struct {
	s *Session
}

// Insert 对应的 SQL 语句一般是这样的：
//	INSERT INTO table_name(col1, col2, col3, ...) VALUES (A1, A2, A3, ...),(B1, B2, B3, ...),...
//	例如:
//		u1 := &User{Name: "Tom", Age: 18}
//		u2 := &User{Name: "Sam", Age: 25}
// 		s.Insert(u1, u2, ...)
func (q *Orm) Insert(values ...interface{}) (int64, error){
	recordValues := make([]interface{}, 0)
	for _, value := range values {
		table := q.s.Model(value).RefTable()
		q.s.clause.Set(Insert, table.Name, table.FieldNames)
		recordValues = append(recordValues, table.FieldValues(value))
	}
	q.s.clause.Set(Values, recordValues...)
	sql, vars := q.s.clause.Build(Insert, Values)

	result, err := q.s.Exec(sql, vars...)
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

// len(where) == 0 则是删除整条记录，否则根据 where条件删除对应的记录。
func (q *Orm) Delete(where ...interface{}) (int64,error) {
	desc,args := where[0].(string),where[1:]
	q.s.Where(desc,args)
	q.s.clause.Set(Delete, q.s.RefTable().Name)
	sql, vars := q.s.clause.Build(Delete,Where)
	result, err := q.s.Exec(sql, vars...)
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

// Update 参数<kv[0]>是待更新的键值对，<kv[1]> 是 Where 的SQL语句，<kv[2:]> 是 Where SQL语句 ? 占位符的具体值。
func (q *Orm) Update(kv ...interface{}) (int64, error) {
	m, ok := kv[0].(map[string]interface{})
	if !ok {
		m = make(map[string]interface{})
		for i := 0; i < len(kv); i += 2 {
			m[kv[i].(string)] = kv[i+1]
		}
	}

	desc,args := kv[1].(string),kv[2:]
	q.s.Where(desc,args)

	q.s.clause.Set(Update, q.s.RefTable().Name, m)
	sql, vars := q.s.clause.Build(Update, Where)
	result, err := q.s.Exec(sql, vars...)
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

// Select 调用方式:
//	var users []User
//	s.Select(&users)
func (q *Orm) Select(sliceType interface{}) error{
	destSlice := reflect.Indirect(reflect.ValueOf(sliceType))
	if destSlice.Type().Kind() != reflect.Slice {
		return errors.New("您输入的参数有误，sliceType必须是 slice/*slice")
	}
	destType := destSlice.Type().Elem() // 获取切片的单个元素的类型 destType
	if destType.Kind() != reflect.Struct{
		return errors.New("您输入的参数有误，sliceType中的元素必须是 struct/*struct")
	}

	// 使用 reflect.New() 方法创建一个 destType 的实例，作为 Model() 的入参，映射出表结构 RefTable()。
	table := q.s.Model(reflect.New(destType).Elem().Interface()).RefTable()
	// 根据表结构，使用 clause 构造出 SELECT 语句，查询到所有符合条件的记录 rows。
	q.s.clause.Set(Select, table.Name, table.FieldNames)
	sql, vars := q.s.clause.Build(Select, Where, OrderBy, Limit)
	rows, err := q.s.Query(sql, vars...)
	if err != nil {
		return err
	}
	//return rows.SliceScan()
	// 遍历每一行记录，利用反射创建 destType 的实例 dest，将 dest 的所有字段平铺开，构造切片 values。
	for rows.Next() {
		dest := reflect.New(destType).Elem()
		var values []interface{}
		for _, name := range table.FieldNames {
			values = append(values, dest.FieldByName(name).Addr().Interface())
		}
		if err := rows.Scan(values...); err != nil { // 将该行记录每一列的值依次赋值给 values 中的每一个字段。
			return err
		}
		destSlice.Set(reflect.Append(destSlice, dest)) // 将 dest 添加到切片 destSlice 中。循环直到所有的记录都添加到切片 destSlice 中。
	}
	return rows.Close()
}

func (q *Orm) Take(structType interface{}) error {
	destStruct := reflect.Indirect(reflect.ValueOf(structType))
	if destStruct.Type().Kind() != reflect.Struct{
		return errors.New("您输入的参数有误，structType必须是 struct/*struct")
	}
	destType := destStruct.Type()
	table := q.s.Model(reflect.New(destType).Interface()).RefTable()
	q.s.clause.Set(Select, table.Name, table.FieldNames)
	sql, vars := q.s.clause.Build(Select, Where, OrderBy, Limit)
	row, err := q.s.QueryRow(sql, vars)
	if err != nil {
		return err
	}
	err = row.StructScan(structType)
	return err
}